package services

import (
	"encoding/json"
	"github.com/gomodule/redigo/redis"
	"log"
	"lottery/comm"
	"lottery/dao"
	"lottery/datasource"
	"lottery/models"
	"strconv"
	"strings"
)

// 定义接口
type GiftService interface {
	GetAll(useCache bool) []models.LtGift
	CountAll() int64
	Get(id int, useCache bool) (*models.LtGift, bool)
	Update(data *models.LtGift, columns []string) error
	Create(data *models.LtGift) error
	Delete(id int) error
	GetAllUse(useCache bool) []models.ObjGiftPrize
	IncrLeftNum(id, num int) (int64, error)
	DecrLeftNum(id, num int) (int64, error)
}

// 小写的，私有，内部使用，实现接口。
type giftService struct {
	dao *dao.GiftDao
}

// 返回一个 GiftService
func NewGiftService() GiftService {
	return &giftService{
		dao: dao.NewGiftDao(datasource.InstanceDB()),
	}
}

// 实现接口函数, 直接调用dao中的实现。
func (s *giftService) GetAll(userCache bool) []models.LtGift {
	// 如果不使用缓存
	if !userCache {
		data, _ := s.dao.GetAll()
		return data
	}
	gifts := s.getAllByCache()
	if len(gifts) < 10 { // 设置缓存
		gifts, _ = s.dao.GetAll() // 数据库读数据
		s.setAllByCache(gifts)    // 设置缓存
	}
	return gifts
}

func (s *giftService) CountAll() int64 {
	gifts := s.GetAll(true)
	return int64(len(gifts))
}

func (s *giftService) Get(id int, useCache bool) (*models.LtGift, bool) {
	if !useCache {
		data, ok := s.dao.Get(id)
		return data, ok
	}
	gifts := s.GetAll(true)
	for _, gift := range gifts { // for 遍历寻找， 数据量比较小的情况下，o（n）时间复杂度也是可以的
		if gift.Id == id {
			return &gift, true
		}
	}
	return nil, false
}

func (s *giftService) Update(data *models.LtGift, columns []string) error {
	// 先更新缓存,再更新数据库
	s.updateByCache(data, columns) // 实际就是删除缓存
	return s.dao.Update(data, columns)
}

func (s *giftService) Create(data *models.LtGift) error {
	s.updateByCache(data, nil)
	return s.dao.Create(data)
}

func (s *giftService) Delete(id int) error {
	// 删除一条数据就全部删除缓存，再删除数据库数据
	data := &models.LtGift{Id: id}
	s.updateByCache(data, nil)
	return s.dao.Delete(id)
}

// 获取到当前可以获取的奖品列表
// 有奖品限定，状态正常，时间期间内
// gtype倒序， displayorder正序
func (s *giftService) GetAllUse(useCache bool) []models.ObjGiftPrize {
	list := make([]models.LtGift, 0)
	if !useCache {
		list = s.dao.GetAllUser()
	} else {
		now := comm.NowUnix()
		// 从redis 全量读取， 再进行判断合法的奖品数据
		gifts := s.GetAll(true)
		for _, gift := range gifts {
			if gift.Id > 0 && gift.SysStatus == 0 &&
				gift.PrizeNum >= 0 &&
				gift.TimeBegin <= now {
				//  && gift.TimeEnd >= now
				list = append(list, gift)
			}
		}
	}
	// 下面处理逻辑不变
	if list != nil {
		// 将数据转换为 ObjGiftPrize 里
		gifts := make([]models.ObjGiftPrize, 0)
		for _, gift := range list {
			codes := strings.Split(gift.PrizeCode, "-")
			if len(codes) == 2 {
				codeA, e1 := strconv.Atoi(codes[0])
				codeB, e2 := strconv.Atoi(codes[1])
				// 当没出现错误，且 B > A > 0
				if e1 == nil && e2 == nil &&
					codeB >= codeA && codeA >= 0 && codeB <= 10000 {
					// 构造出来新模型
					data := models.ObjGiftPrize{
						Id:           gift.Id,
						Title:        gift.Title,
						PrizeNum:     gift.PrizeNum,
						LeftNum:      gift.LeftNum,
						PrizeCodeA:   codeA,
						PrizeCodeB:   codeB,
						Img:          gift.Img,
						Displayorder: gift.Displayorder,
						Gtype:        gift.Gtype,
						Gdata:        gift.Gdata,
					}
					gifts = append(gifts, data)
				}
			}
		}
		return gifts
	} else {
		return []models.ObjGiftPrize{}
	}
}

func (s *giftService) IncrLeftNum(id, num int) (int64, error) {
	rows, err := s.dao.IncrLeftNum(id, num)
	// 数据库更新成功，这里再更新下缓存中的奖品数量
	if rows > 0 && err == nil {
		s.updateByCache(&models.LtGift{}, nil)
	}
	return rows, err
}

func (s *giftService) DecrLeftNum(id, num int) (int64, error) {
	rows, err := s.dao.DecrLeftNum(id, num)
	// 数据库更新成功，这里再更新下缓存中的奖品数量, 保持一致
	if rows > 0 && err == nil {
		s.updateByCache(&models.LtGift{}, nil)
	}
	return rows, err
}

// 缓存中返回所有数据: 读取缓存
func (s *giftService) getAllByCache() []models.LtGift {
	// 连接redis数据库
	rds := datasource.InstanceCache()
	// 拿到对应的数据： json 序列化存到redis里去的
	str, err := redis.String(rds.Do("GET", "allgift"))
	if err != nil {
		// 打印错误日志
		log.Println("gift_service.getAllByCache GET key = allgift", "error = ", err)
		return nil
	}
	if str == "" {
		return nil
	}
	// 解析json格式数据: 值是个接口类型，解析写入接口类型，查看文档会自动做一些类型转换
	datalist := []map[string]interface{}{}
	err = json.Unmarshal([]byte(str), &datalist)
	if err != nil {
		log.Println("gift_service.getAllByCache json.Unmarshal error = ", err)
		return nil
	}
	// 转换为 LtGift结构体格式
	gifts := make([]models.LtGift, len(datalist))
	for i := 0; i < len(datalist); i++ {
		// 得到一个结构体数据
		data := datalist[i]
		// 从数据中获取接口类型值
		id := comm.GetInt64FromMap(data, "Id", 0)
		if id < 0 {
			gifts[i] = models.LtGift{}
		} else {
			// 拼错数据， 也可以直接使用结构体来直接umasharl, 虽然报错，但是数据都是该类型 0值填充，和下面作用一样
			gift := models.LtGift{
				Id:           int(id),
				Title:        comm.GetStringFromMap(data, "Title", ""),
				PrizeNum:     int(comm.GetInt64FromMap(data, "PrizeNum", 0)),
				LeftNum:      int(comm.GetInt64FromMap(data, "LeftNum", 0)),
				PrizeCode:    comm.GetStringFromMap(data, "PrizeCode", ""),
				PrizeTime:    int(comm.GetInt64FromMap(data, "PrizeTime", 0)),
				Img:          comm.GetStringFromMap(data, "Img", ""),
				Displayorder: int(comm.GetInt64FromMap(data, "Displayorder", 0)),
				Gtype:        int(comm.GetInt64FromMap(data, "Gtype", 0)),
				Gdata:        comm.GetStringFromMap(data, "Gdata", ""),
				TimeBegin:    int(comm.GetInt64FromMap(data, "TimeBegin", 0)),
				TimeEnd:      int(comm.GetInt64FromMap(data, "TimeEnd", 0)),
				//PrizeData:    comm.GetStringFromMap(data, "PrizeData", ""),
				PrizeBegin: int(comm.GetInt64FromMap(data, "PrizeBegin", 0)),
				PrizeEnd:   int(comm.GetInt64FromMap(data, "PrizeEnd", 0)),
				SysStatus:  int(comm.GetInt64FromMap(data, "SysStatus", 0)),
				SysCreated: int(comm.GetInt64FromMap(data, "SysCreated", 0)),
				SysUpdated: int(comm.GetInt64FromMap(data, "SysUpdated", 0)),
				SysIp:      comm.GetStringFromMap(data, "SysIp", ""),
			}
			gifts[i] = gift
		}
	}
	return gifts
}

// 设置缓存
// 同样可以使用结构体
func (s *giftService) setAllByCache(gifts []models.LtGift) {
	strValue := ""
	if len(gifts) > 0 {
		datalist := make([]map[string]interface{}, len(gifts))
		// 格式转换
		for i := 0; i < len(gifts); i++ {
			gift := gifts[i]
			data := make(map[string]interface{}, 0)
			data["Id"] = gift.Id
			data["Title"] = gift.Title
			data["PrizeNum"] = gift.PrizeNum
			data["LeftNum"] = gift.LeftNum
			data["PrizeCode"] = gift.PrizeCode
			data["PrizeTime"] = gift.PrizeTime
			data["Img"] = gift.Img
			data["Displayorder"] = gift.Displayorder
			data["Gtype"] = gift.Gtype
			data["Gdata"] = gift.Gdata
			data["TimeBegin"] = gift.TimeBegin
			data["TimeEnd"] = gift.TimeEnd
			//data["PrizeData"] = gift.PrizeData
			data["PrizeBegin"] = gift.PrizeBegin
			data["PrizeEnd"] = gift.PrizeEnd
			data["SysStatus"] = gift.SysStatus
			data["SysCreated"] = gift.SysCreated
			data["SysUpdated"] = gift.SysUpdated
			data["SysIp"] = gift.SysIp
			datalist[i] = data
		}
		str, err := json.Marshal(datalist)
		if err != nil {
			log.Println("gift_service.setAllByCache json.Marshal error = ", err)
		}
		strValue = string(str)
	}
	// 存入redis
	key := "allgift"
	rds := datasource.InstanceCache()
	_, err := rds.Do("SET", key, strValue)
	if err != nil {
		log.Println("gift_service.setAllByCache redis SET key = ", key,
			", error = ", err)
	}
}

// 实际上一个删除操作，删除掉缓存，再生成的过程，就是更新
func (s *giftService) updateByCache(data *models.LtGift, columns []string) {
	// 数据不存在，根本不需要更新(删除)
	// || data.Id <= 0
	if data == nil {
		return
	}
	key := "allgift"
	rds := datasource.InstanceCache()
	rds.Do("DEL", key)
}
